package com.zhl.framework.core.common.jdk;

import com.zhl.framework.core.common.RpcInvocation;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.UUID;
import java.util.concurrent.TimeoutException;

import static com.zhl.framework.core.common.cache.CommonClientCache.RESP_MAP;
import static com.zhl.framework.core.common.cache.CommonClientCache.SEND_QUEUE;

/**
 * @Author: ZhangLu In DLUT
 * @since: 1.0.0
 * @Description: 各种代理工厂统一使用这个 InvocationHandler, 作用是将方法功能增强，通过Netty发送到服务端
 */
public class JDKClientInvocationHandler implements InvocationHandler {
    private final static Object OBJECT = new Object();

    private Class<?> clazz;

    public JDKClientInvocationHandler(Class<?> clazz) {
        this.clazz = clazz;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        /**
         * 根据调用的方法，创建远程调用对象 RpcInvocation 并给对象填充属性
         */
        RpcInvocation rpcInvocation = new RpcInvocation();
        rpcInvocation.setArgs(args);
        rpcInvocation.setTargetMethod(method.getName());
        rpcInvocation.setTargetServiceName(clazz.getName());
        rpcInvocation.setUuid(UUID.randomUUID().toString());
        // 将调用的信息存入缓存当中
        SEND_QUEUE.add(rpcInvocation);
        // 将调用的 uuid 存入结果缓存中
        RESP_MAP.put(rpcInvocation.getUuid(), OBJECT);
        long beginTime = System.currentTimeMillis();
        // 超时等待 RPC 调用结果,等待 3 秒钟
        while (System.currentTimeMillis()-beginTime <= 3*1000){
            // 不断地从结果集中获取结果
            Object o = RESP_MAP.get(rpcInvocation.getUuid());
            // 判断结果是否已经返回，返回的结果一定是 RpcInvocation 类型的对象
            if (o instanceof RpcInvocation) {
                return ((RpcInvocation) o).getResponse();
            }

        }
        // TODO 这是容错策略的一种，是一个超时等待策略，可以进行优化
        throw new TimeoutException("客户端等待服务端响应超时！");
    }
}
